package com.cbh.amadeus.server.core;

import com.cbh.amadeus.common.handler.ExceptionHandler;
import com.cbh.amadeus.common.handler.HeartBitHandler;
import com.cbh.amadeus.common.handler.IdentifyHandler;
import com.cbh.amadeus.common.protocol.AmadeusMessageProtocol;
import com.cbh.amadeus.server.handler.*;
import com.cbh.amadeus.server.provider.component.PublisherComponent;
import com.cbh.amadeus.server.utils.MessageApproachManage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;

import javax.annotation.Resource;

/**
 * @author ChenBiHui
 * @date 2025/4/25
 */
@Slf4j
public class Publisher implements InitializingBean, DisposableBean {

    public static final EventLoopGroup DEFAULT = new DefaultEventLoopGroup(
            Runtime.getRuntime().availableProcessors(),
            new DefaultThreadFactory("amadeus pd")
    );
    @Resource
    private PublisherComponent component;

    public static void bootstrap(PublisherComponent component) {
        NioEventLoopGroup boss = new NioEventLoopGroup(new DefaultThreadFactory("amadeus"));
        NioEventLoopGroup work = new NioEventLoopGroup(Runtime.getRuntime().availableProcessors(), new DefaultThreadFactory("amadeus pw"));
        ServerBootstrap server = new ServerBootstrap();
        ChannelFuture future = server.group(boss, work)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, true)
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) {
                        ch.pipeline()
                                .addLast(AmadeusMessageProtocol.frameDecoder())
                                .addLast(AmadeusMessageProtocol.protocol())
                                .addLast(new IdleStateHandler(component.getConfig().getReaderIdleTimeSeconds(), component.getConfig().getWriterIdleTimeSeconds(), 0))
                                .addLast(new InactiveHandler())
                                .addLast(new IdentifyHandler())
                                .addLast(new HeartBitHandler(component.getConfig().getReaderIdle(), component.getConfig().getWriterIdle()))
                                .addLast(new RegisterHandler())
                                .addLast(new PublishHandler())
                                .addLast(new ResendHandler(component))
                                .addLast(new AckHandler(component))
                                .addLast(new OnlyConnectSendHandler())
                                .addLast(new ExceptionHandler())
                        ;
                    }
                })
                .bind(component.getConfig().getPort())
                .addListener(item -> {
                    if (item.isSuccess()) {
                        log.info(
                                "===== Amadeus Message center server is ready in [{}] =====",
                                component.getConfig().getPort()
                        );
                        MessageApproachManage.start();
                    } else {
                        log.info("===== Amadeus Message center server is not ready =====");
                    }
                });
        future.channel()
                .closeFuture()
                .addListener(item -> {
                    boss.shutdownGracefully();
                    work.shutdownGracefully();
                });
    }

    @Override
    public void afterPropertiesSet() {
        Publisher.bootstrap(component);
    }

    @Override
    public void destroy() {
        DEFAULT.shutdownGracefully();
    }
}
